package cc.shacocloud.luanniao.web.controller.interceptor

import cc.shacocloud.luanniao.core.exception.ForbiddenException
import cc.shacocloud.luanniao.core.exception.UnauthorizedException
import cc.shacocloud.luanniao.core.model.constant.UserAuthRoleEnum
import cc.shacocloud.luanniao.core.model.po.UserPo
import cc.shacocloud.luanniao.restful.RequestMappingHandlerChain
import cc.shacocloud.luanniao.restful.RequestMappingHandlerInterceptor
import cc.shacocloud.luanniao.restful.RequestMappingHandlerInterceptorAdapter
import cc.shacocloud.luanniao.web.model.mo.SessionMo
import cc.shacocloud.luanniao.web.model.mo.UserSessionMo
import cc.shacocloud.luanniao.web.service.SessionService
import kotlinx.coroutines.reactor.awaitSingle
import kotlinx.coroutines.reactor.awaitSingleOrNull
import kotlinx.coroutines.reactor.mono
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.core.Ordered
import org.springframework.core.annotation.AnnotatedElementUtils
import org.springframework.stereotype.Service
import org.springframework.web.method.HandlerMethod
import org.springframework.web.reactive.HandlerAdapter
import org.springframework.web.reactive.HandlerResult
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono
import kotlin.jvm.optionals.getOrNull

/**
 * 认证拦截器
 *
 * 基于 [HandlerAdapter] 实现得认证拦截器。见 [RequestMappingHandlerInterceptorAdapter]
 * @author 思追(shaco)
 */
@Service
class AuthInterceptor(
    val sessionService: SessionService,
) : RequestMappingHandlerInterceptor {

    companion object {

        private val log: Logger = LoggerFactory.getLogger(AuthInterceptor::class.java)

        /**
         * 上问下中存储 [UserPo] 的键
         */
        val USER_CONTEXT_KEY = "${AuthInterceptor::class.java.canonicalName}.user"

        /**
         * 当前用户信息
         */
        fun ServerWebExchange.currentUser(): UserPo {
            return attributes[USER_CONTEXT_KEY] as UserPo? ?: throw UnauthorizedException()
        }
    }

    override fun handle(
        exchange: ServerWebExchange,
        handler: HandlerMethod,
        chain: RequestMappingHandlerChain,
    ): Mono<HandlerResult> = mono {
        // 获取当前接口允许的用户认证角色访问权限，如果未设置则默认为 UserAuthRoleEnum.USER
        val userAuthRole = handler.getMethodAnnotation(Auth::class.java)?.role
            ?: AnnotatedElementUtils.findMergedAnnotation(handler.beanType, Auth::class.java)?.role
            ?: let { UserAuthRoleEnum.USER }

        // 获取当前用户的会话
        val currentSession = sessionService.currentSession(exchange)
        auth(userAuthRole, currentSession)

        if (currentSession.isLogin()) {
            exchange.attributes[USER_CONTEXT_KEY] = sessionService.getCurrentUser(exchange)
        }

        chain.next(exchange, handler).awaitSingleOrNull()
    }

    /**
     * 认证方法
     */
    suspend fun auth(
        needAuthRole: UserAuthRoleEnum,
        currentSession: SessionMo,
    ) {
        val currentUserRole = currentSession.role

        // 包含则认证通过
        if (currentUserRole.level < needAuthRole.level) {

            // 已登录用户抛出无权限异常
            if (currentSession.isLogin()) {
                throw ForbiddenException()
            }
            // 如果未登录用户则抛出未登录异常
            else {
                throw UnauthorizedException()
            }
        }
    }

    override fun getOrder(): Int {
        return Ordered.HIGHEST_PRECEDENCE
    }

}